بر پایپ لاین های Scikit-learn مسلط شوید تا گردش کار یادگیری ماشین خود را ساده کنید. یاد بگیرید که پیش پردازش، آموزش مدل و تنظیم هایپرپارامترها را برای مدل های قوی، قابل تکرار و آماده تولید خودکار کنید.
پایپ لاین Scikit-learn: راهنمای نهایی برای اتوماسیون گردش کار ML
در دنیای یادگیری ماشین، ساخت یک مدل اغلب به عنوان آخرین گام پر زرق و برق به تصویر کشیده می شود. با این حال، دانشمندان داده و مهندسان ML باتجربه می دانند که مسیر رسیدن به یک مدل قوی با مجموعه ای از مراحل حیاتی، اغلب تکراری و مستعد خطا هموار شده است: پاکسازی داده ها، مقیاس بندی ویژگی ها، رمزگذاری متغیرهای دسته ای و موارد دیگر. مدیریت این مراحل به صورت جداگانه برای مجموعه های آموزش، اعتبارسنجی و آزمایش می تواند به سرعت به یک کابوس لجستیکی تبدیل شود و منجر به اشکالات ظریف و از همه خطرناک تر، نشت داده شود.
اینجاست که پایپ لاین Scikit-learn به کمک می آید. این فقط یک راحتی نیست. این یک ابزار اساسی برای ساخت سیستم های یادگیری ماشین حرفه ای، قابل تکرار و آماده تولید است. این راهنمای جامع شما را در تمام مواردی که برای تسلط بر پایپ لاین های Scikit-learn نیاز دارید، از مفاهیم اولیه تا تکنیک های پیشرفته، راهنمایی می کند.
مشکل: گردش کار دستی یادگیری ماشین
بیایید یک کار یادگیری نظارت شده معمولی را در نظر بگیریم. حتی قبل از اینکه بتوانید model.fit() را فراخوانی کنید، باید داده های خود را آماده کنید. یک گردش کار استاندارد ممکن است به این شکل باشد:
- تقسیم داده ها: مجموعه داده خود را به مجموعه های آموزش و آزمایش تقسیم کنید. این اولین و مهمترین گام برای اطمینان از این است که می توانید عملکرد مدل خود را بر روی داده های مشاهده نشده ارزیابی کنید.
- مدیریت مقادیر از دست رفته: داده های از دست رفته در مجموعه آموزش خود را شناسایی و جایگزین کنید (به عنوان مثال، با استفاده از میانگین، میانه یا یک مقدار ثابت).
- رمزگذاری ویژگی های دسته ای: ستون های غیر عددی مانند 'کشور' یا 'دسته بندی محصول' را با استفاده از تکنیک هایی مانند رمزگذاری یک-هات یا رمزگذاری ترتیبی به یک فرمت عددی تبدیل کنید.
- مقیاس بندی ویژگی های عددی: با استفاده از روش هایی مانند استانداردسازی (
StandardScaler) یا نرمال سازی (MinMaxScaler)، تمام ویژگی های عددی را به یک مقیاس مشابه بیاورید. این برای بسیاری از الگوریتم ها مانند SVM ها، رگرسیون لجستیک و شبکه های عصبی بسیار مهم است. - آموزش مدل: در نهایت، مدل یادگیری ماشین انتخابی خود را روی داده های آموزشی از پیش پردازش شده برازش دهید.
اکنون، هنگامی که می خواهید روی مجموعه آزمایشی خود (یا داده های جدید و مشاهده نشده) پیش بینی کنید، باید دقیقاً مراحل پیش پردازش را تکرار کنید. شما باید همان استراتژی جایگزینی (با استفاده از مقدار محاسبه شده از مجموعه آموزشی)، همان طرح رمزگذاری و همان پارامترهای مقیاس بندی را اعمال کنید. پیگیری دستی تمام این مبدل های برازش شده، خسته کننده و یک منبع اصلی خطا است.
بزرگترین خطر در اینجا نشت داده است. این زمانی رخ می دهد که اطلاعات از مجموعه آزمایشی به طور ناخواسته به فرآیند آموزش نشت کند. به عنوان مثال، اگر میانگین را برای جایگزینی یا پارامترهای مقیاس بندی را از کل مجموعه داده قبل از تقسیم محاسبه کنید، مدل شما به طور ضمنی از داده های آزمایشی یاد می گیرد. این منجر به یک تخمین عملکرد بیش از حد خوش بینانه و مدلی می شود که در دنیای واقعی به طرز فجیعی شکست می خورد.
معرفی پایپ لاین های Scikit-learn: راه حل خودکار
یک پایپ لاین Scikit-learn یک شی است که چندین مرحله تبدیل داده و یک تخمین گر نهایی (مانند یک طبقه بندی کننده یا رگرسیون) را به یک شی واحد و یکپارچه زنجیره می کند. می توانید آن را به عنوان یک خط مونتاژ برای داده های خود در نظر بگیرید.
هنگامی که .fit() را روی یک پایپ لاین فراخوانی می کنید، به طور متوالی fit_transform() را به هر مرحله میانی روی داده های آموزشی اعمال می کند و خروجی یک مرحله را به عنوان ورودی به مرحله بعدی منتقل می کند. در نهایت، .fit() را روی آخرین مرحله، یعنی تخمین گر، فراخوانی می کند. هنگامی که .predict() یا .transform() را روی پایپ لاین فراخوانی می کنید، فقط روش .transform() هر مرحله میانی را قبل از پیش بینی با تخمین گر نهایی روی داده های جدید اعمال می کند.
مزایای کلیدی استفاده از پایپ لاین ها
- جلوگیری از نشت داده: این مهمترین مزیت است. با کپسوله کردن تمام پیش پردازش ها در داخل پایپ لاین، اطمینان حاصل می کنید که تبدیل ها فقط از داده های آموزشی در طول اعتبارسنجی متقابل آموخته می شوند و به درستی روی داده های اعتبارسنجی/آزمایشی اعمال می شوند.
- سادگی و سازماندهی: کل گردش کار شما، از داده های خام تا یک مدل آموزش دیده، در یک شی واحد متراکم می شود. این امر کد شما را تمیزتر، خواناتر و آسان تر برای مدیریت می کند.
- قابلیت تکرار: یک شی پایپ لاین کل فرآیند مدل سازی شما را کپسوله می کند. شما می توانید به راحتی این شی واحد را ذخیره کنید (به عنوان مثال، با استفاده از `joblib` یا `pickle`) و بعداً آن را بارگیری کنید تا پیش بینی کنید، و اطمینان حاصل کنید که دقیقاً همان مراحل هر بار دنبال می شوند.
- کارایی در جستجوی شبکه: می توانید تنظیم هایپرپارامتر را در کل پایپ لاین به طور همزمان انجام دهید و بهترین پارامترها را هم برای مراحل پیش پردازش و هم برای مدل نهایی به طور همزمان پیدا کنید. ما این ویژگی قدرتمند را بعداً بررسی خواهیم کرد.
ساخت اولین پایپ لاین ساده خود
بیایید با یک مثال اساسی شروع کنیم. تصور کنید یک مجموعه داده عددی داریم و می خواهیم قبل از آموزش یک مدل رگرسیون لجستیک، داده ها را مقیاس بندی کنیم. در اینجا نحوه ساخت یک پایپ لاین برای آن آورده شده است.
ابتدا، محیط خود را تنظیم کرده و مقداری داده نمونه ایجاد می کنیم.
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.metrics import accuracy_score
# Generate some sample data
X, y = np.random.rand(100, 5) * 10, (np.random.rand(100) > 0.5).astype(int)
# Split data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
اکنون، پایپ لاین خود را تعریف می کنیم. یک پایپ لاین با ارائه لیستی از مراحل ایجاد می شود. هر مرحله یک تاپل است که شامل یک نام (رشته ای به انتخاب شما) و خود شی مبدل یا تخمین گر است.
# Create the pipeline steps
steps = [
('scaler', StandardScaler()),
('classifier', LogisticRegression())
]
# Create the Pipeline object
pipe = Pipeline(steps)
# Now, you can treat the 'pipe' object as if it were a regular model.
# Let's train it on our training data.
pipe.fit(X_train, y_train)
# Make predictions on the test data
y_pred = pipe.predict(X_test)
# Evaluate the model
accuracy = accuracy_score(y_test, y_pred)
print(f"Pipeline Accuracy: {accuracy:.4f}")
همین! فقط در چند خط، ما مقیاس بندی و طبقه بندی را ترکیب کرده ایم. Scikit-learn تمام منطق میانی را مدیریت می کند. وقتی pipe.fit(X_train, y_train) فراخوانی می شود، ابتدا StandardScaler().fit_transform(X_train) را فراخوانی می کند و سپس نتیجه را به LogisticRegression().fit() منتقل می کند. وقتی pipe.predict(X_test) فراخوانی می شود، قبل از پیش بینی با مدل رگرسیون لجستیک، مقیاس دهنده از قبل برازش شده را با استفاده از StandardScaler().transform(X_test) اعمال می کند.
مدیریت داده های ناهمگن: ColumnTransformer
مجموعه داده های دنیای واقعی به ندرت ساده هستند. آنها اغلب حاوی ترکیبی از انواع داده ها هستند: ستون های عددی که نیاز به مقیاس بندی دارند، ستون های دسته ای که نیاز به رمزگذاری دارند و شاید ستون های متنی که نیاز به بردارسازی دارند. یک پایپ لاین متوالی ساده برای این کافی نیست، زیرا شما باید تبدیل های مختلف را روی ستون های مختلف اعمال کنید.
اینجاست که ColumnTransformer می درخشد. این به شما امکان می دهد تبدیل های مختلف را روی زیرمجموعه های مختلف ستون ها در داده های خود اعمال کنید و سپس هوشمندانه نتایج را به هم متصل کنید. این ابزار عالی برای استفاده به عنوان یک مرحله پیش پردازش در یک پایپ لاین بزرگتر است.
مثال: ترکیب ویژگی های عددی و دسته ای
بیایید یک مجموعه داده واقعی تر با هر دو ویژگی عددی و دسته ای با استفاده از پانداها ایجاد کنیم.
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
from sklearn.impute import SimpleImputer
# Create a sample DataFrame
data = {
'age': [25, 30, 45, 35, 50, np.nan, 22],
'salary': [50000, 60000, 120000, 80000, 150000, 75000, 45000],
'country': ['USA', 'Canada', 'USA', 'UK', 'Canada', 'USA', 'UK'],
'purchased': [0, 1, 1, 0, 1, 1, 0]
}
df = pd.DataFrame(data)
X = df.drop('purchased', axis=1)
y = df['purchased']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Identify numerical and categorical columns
numerical_features = ['age', 'salary']
categorical_features = ['country']
استراتژی پیش پردازش ما به این صورت خواهد بود:
- برای ستون های عددی (
age،salary): مقادیر از دست رفته را با میانه جایگزین کنید، سپس آنها را مقیاس بندی کنید. - برای ستون های دسته ای (
country): مقادیر از دست رفته را با پرتکرارترین دسته جایگزین کنید، سپس آنها را به صورت یک-هات رمزگذاری کنید.
ما می توانیم این مراحل را با استفاده از دو پایپ لاین کوچک جداگانه تعریف کنیم.
# Create a pipeline for numerical features
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())
])
# Create a pipeline for categorical features
categorical_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='most_frequent')),
('onehot', OneHotEncoder(handle_unknown='ignore'))
])
اکنون، ما از `ColumnTransformer` برای اعمال این پایپ لاین ها روی ستون های صحیح استفاده می کنیم.
# Create the preprocessor with ColumnTransformer
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numerical_features),
('cat', categorical_transformer, categorical_features)
])
ColumnTransformer لیستی از `transformers` را می گیرد. هر مبدل یک تاپل است که شامل یک نام، شی مبدل (که می تواند خود یک پایپ لاین باشد) و لیستی از نام ستون ها برای اعمال آن است.
در نهایت، می توانیم این `preprocessor` را به عنوان اولین مرحله در پایپ لاین اصلی خود قرار دهیم، و پس از آن تخمین گر نهایی خود را قرار دهیم.
from sklearn.ensemble import RandomForestClassifier
# Create the full pipeline
full_pipeline = Pipeline(steps=[
('preprocessor', preprocessor),
('classifier', RandomForestClassifier(random_state=42))
])
# Train and evaluate the full pipeline
full_pipeline.fit(X_train, y_train)
print("Model score on test data:", full_pipeline.score(X_test, y_test))
# You can now make predictions on new raw data
new_data = pd.DataFrame({
'age': [40, 28],
'salary': [90000, 55000],
'country': ['USA', 'Germany'] # 'Germany' is an unknown category
})
predictions = full_pipeline.predict(new_data)
print("Predictions for new data:", predictions)
توجه کنید که چگونه این به طرز ظریفی یک گردش کار پیچیده را مدیریت می کند. پارامتر `handle_unknown='ignore'` در `OneHotEncoder` به ویژه برای سیستم های تولید مفید است، زیرا از بروز خطا هنگام ظاهر شدن دسته بندی های جدید و مشاهده نشده در داده ها جلوگیری می کند.
تکنیک های پیشرفته پایپ لاین
پایپ لاین ها قدرت و انعطاف پذیری بیشتری را ارائه می دهند. بیایید برخی از ویژگی های پیشرفته را که برای پروژه های یادگیری ماشین حرفه ای ضروری هستند، بررسی کنیم.
ایجاد مبدل های سفارشی
گاهی اوقات، مبدل های داخلی Scikit-learn کافی نیستند. ممکن است لازم باشد یک تبدیل خاص دامنه، مانند استخراج لگاریتم یک ویژگی یا ترکیب دو ویژگی در یک ویژگی جدید، انجام دهید. شما می توانید به راحتی مبدل های سفارشی خود را ایجاد کنید که به طور یکپارچه در یک پایپ لاین ادغام می شوند.
برای انجام این کار، کلاسی ایجاد می کنید که از `BaseEstimator` و `TransformerMixin` به ارث می رسد. فقط باید روش های `fit()` و `transform()` (و در صورت نیاز `__init__()`) را پیاده سازی کنید.
بیایید یک مبدل ایجاد کنیم که یک ویژگی جدید اضافه می کند: نسبت `salary` به `age`.
from sklearn.base import BaseEstimator, TransformerMixin
# Define column indices (can also pass names)
age_ix, salary_ix = 0, 1
class FeatureRatioAdder(BaseEstimator, TransformerMixin):
def __init__(self):
pass # No parameters to set
def fit(self, X, y=None):
return self # Nothing to learn during fit, so just return self
def transform(self, X):
salary_age_ratio = X[:, salary_ix] / X[:, age_ix]
return np.c_[X, salary_age_ratio] # Concatenate original X with new feature
سپس می توانید این مبدل سفارشی را در پایپ لاین پردازش عددی خود قرار دهید:
numeric_transformer_with_custom = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('ratio_adder', FeatureRatioAdder()), # Our custom transformer
('scaler', StandardScaler())
])
این سطح از سفارشی سازی به شما امکان می دهد تمام منطق مهندسی ویژگی خود را در داخل پایپ لاین کپسوله کنید و گردش کار خود را بسیار قابل حمل و قابل تکرار کنید.
تنظیم هایپرپارامتر با پایپ لاین ها با استفاده از GridSearchCV
این احتمالاً یکی از قدرتمندترین کاربردهای پایپ لاین ها است. شما می توانید بهترین هایپرپارامترها را برای کل گردش کار خود، از جمله مراحل پیش پردازش و مدل نهایی، به طور همزمان جستجو کنید.
برای مشخص کردن کدام پارامترها را تنظیم کنید، از یک نحو خاص استفاده می کنید: `step_name__parameter_name`.
بیایید مثال قبلی خود را گسترش دهیم و هایپرپارامترها را هم برای جایگزین کننده در پیش پردازنده و هم برای `RandomForestClassifier` تنظیم کنیم.
from sklearn.model_selection import GridSearchCV
# We use the 'full_pipeline' from the ColumnTransformer example
# Define the parameter grid
param_grid = {
'preprocessor__num__imputer__strategy': ['mean', 'median'],
'classifier__n_estimators': [50, 100, 200],
'classifier__max_depth': [None, 10, 20],
'classifier__min_samples_leaf': [1, 2, 4]
}
# Create the GridSearchCV object
grid_search = GridSearchCV(full_pipeline, param_grid, cv=5, verbose=1, n_jobs=-1)
# Fit it to the data
grid_search.fit(X_train, y_train)
# Print the best parameters and score
print("Best parameters found: ", grid_search.best_params_)
print("Best cross-validation score: ", grid_search.best_score_)
# The best estimator is already refitted on the whole training data
best_model = grid_search.best_estimator_
print("Test set score with best model: ", best_model.score(X_test, y_test))
به کلیدهای موجود در `param_grid` دقت کنید:
'preprocessor__num__imputer__strategy': این پارامتر `strategy` از مرحله `SimpleImputer` به نام `imputer` را در داخل پایپ لاین عددی به نام `num` هدف قرار می دهد که خود در داخل `ColumnTransformer` به نام `preprocessor` قرار دارد.'classifier__n_estimators': این پارامتر `n_estimators` از تخمین گر نهایی به نام `classifier` را هدف قرار می دهد.
با انجام این کار، `GridSearchCV` به درستی تمام ترکیبات را امتحان می کند و مجموعه بهینه ای از پارامترها را برای کل گردش کار پیدا می کند، و به طور کامل از نشت داده در طول فرآیند تنظیم جلوگیری می کند زیرا تمام پیش پردازش ها در داخل هر چین اعتبارسنجی متقابل انجام می شود.
تجسم و بررسی پایپ لاین خود
استدلال در مورد پایپ لاین های پیچیده می تواند دشوار شود. Scikit-learn یک راه عالی برای تجسم آنها ارائه می دهد. از نسخه 0.23، می توانید یک نمایش HTML تعاملی دریافت کنید.
from sklearn import set_config
# Set display to 'diagram' to get the visual representation
set_config(display='diagram')
# Now, simply displaying the pipeline object in a Jupyter Notebook or similar environment will render it
full_pipeline
این یک نمودار ایجاد می کند که جریان داده ها را از طریق هر مبدل و تخمین گر، همراه با نام آنها نشان می دهد. این برای اشکال زدایی، به اشتراک گذاری کار خود و درک ساختار مدل شما فوق العاده مفید است.
همچنین می توانید با استفاده از نام آنها به مراحل جداگانه یک پایپ لاین برازش شده دسترسی داشته باشید:
# Access the final classifier of the fitted pipeline
final_classifier = full_pipeline.named_steps['classifier']
print("Feature importances:", final_classifier.feature_importances_)
# Access the OneHotEncoder to see the learned categories
onehot_encoder = full_pipeline.named_steps['preprocessor'].named_transformers_['cat'].named_steps['onehot']
print("Categorical features learned:", onehot_encoder.categories_)
اشتباهات رایج و بهترین روش ها
- برازش روی داده های اشتباه: همیشه، همیشه پایپ لاین خود را فقط روی داده های آموزشی برازش دهید. هرگز آن را روی کل مجموعه داده یا مجموعه آزمایشی برازش ندهید. این قانون اصلی برای جلوگیری از نشت داده است.
- فرمت های داده: مراقب فرمت داده مورد انتظار هر مرحله باشید. برخی از مبدل ها (مانند موارد موجود در مثال سفارشی ما) ممکن است با آرایه های NumPy کار کنند، در حالی که استفاده از برخی دیگر با پانداهای DataFrames راحت تر است. Scikit-learn به طور کلی در مدیریت این موضوع خوب است، اما این چیزی است که باید از آن آگاه بود، به خصوص با مبدل های سفارشی.
- ذخیره و بارگیری پایپ لاین ها: برای استقرار مدل خود، باید پایپ لاین برازش شده را ذخیره کنید. روش استاندارد برای انجام این کار در اکوسیستم پایتون با `joblib` یا `pickle` است. `joblib` اغلب برای اشیایی که آرایه های بزرگ NumPy را حمل می کنند کارآمدتر است.
import joblib # Save the pipeline joblib.dump(full_pipeline, 'my_model_pipeline.joblib') # Load the pipeline later loaded_pipeline = joblib.load('my_model_pipeline.joblib') # Make predictions with the loaded model loaded_pipeline.predict(new_data) - استفاده از نام های توصیفی: به مراحل پایپ لاین و اجزای `ColumnTransformer` نام های واضح و توصیفی بدهید (به عنوان مثال، 'numeric_imputer'، 'categorical_encoder'، 'svm_classifier'). این امر کد شما را خواناتر می کند و تنظیم هایپرپارامتر و اشکال زدایی را ساده می کند.
نتیجه گیری: چرا پایپ لاین ها برای ML حرفه ای غیرقابل مذاکره هستند
پایپ لاین های Scikit-learn فقط ابزاری برای نوشتن کد مرتب تر نیستند. آنها نشان دهنده یک تغییر پارادایم از اسکریپت نویسی دستی و مستعد خطا به یک رویکرد سیستماتیک، قوی و قابل تکرار برای یادگیری ماشین هستند. آنها ستون فقرات شیوه های مهندسی ML سالم هستند.
با اتخاذ پایپ لاین ها، به دست می آورید:
- استحکام: شما رایج ترین منبع خطا در پروژه های یادگیری ماشین - نشت داده - را حذف می کنید.
- کارایی: شما کل گردش کار خود، از مهندسی ویژگی تا تنظیم هایپرپارامتر، را در یک واحد واحد و منسجم ساده می کنید.
- قابلیت تکرار: شما یک شیء واحد و سریال شدنی ایجاد می کنید که حاوی کل منطق مدل شما است و استقرار و اشتراک گذاری آن را آسان می کند.
اگر در مورد ساخت مدل های یادگیری ماشین که به طور قابل اعتماد در دنیای واقعی کار می کنند جدی هستید، تسلط بر پایپ لاین های Scikit-learn اختیاری نیست - ضروری است. از امروز شروع به گنجاندن آنها در پروژه های خود کنید و مدل های بهتر و قابل اعتمادتر را سریعتر از همیشه خواهید ساخت.